package com.dotandmedia.action;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import org.apache.struts2.interceptor.SessionAware;

import com.dotmedia.constants.PartTypesConstants;
import com.dotmedia.dboperations.AdDatabaseOperations;
import com.dotmedia.dboperations.AdPartsDatabaseOperations;
import com.dotmedia.dboperations.AdTemplateDatabaseOperations;
import com.dotmedia.dboperations.AdsizeDatabaseOperations;
import com.dotmedia.dboperations.AdvertiserDatabaseOperations;
import com.dotmedia.dboperations.PartTypesDatabaseOperations;
import com.dotmedia.dboperations.SystemDatabaseOperations;
import com.dotmedia.entities.Ad;
import com.dotmedia.entities.AdParts;
import com.dotmedia.entities.AdTemplate;
import com.dotmedia.entities.Adsize;
import com.dotmedia.entities.Advertiser;
import com.dotmedia.entities.PartTypes;
import com.dotmedia.entities.SystemEntity;
import com.opensymphony.xwork2.ActionSupport;

/**
 * This is the action class which will add a new ad in the database
 * 
 * @author siddharthsaha
 * 
 */
public class AddNewAdAction extends ActionSupport implements SessionAware, ServletRequestAware, ServletResponseAware {

	private static final long serialVersionUID = -2772018280680553775L;
	private static Logger logger = Logger.getLogger("AddNewAdAction");
	private static final String START_BRACKET = "[";
	private static final String STATUS_ACTIVE = "active";
	private static final String STATUS_INACTIVE = "inactive";
	private static final String STATUS_ARCHIVED = "archived";

	private static final String NUMERIC_MACRO_PARAMETER = "numeric_";
	private static final String GENUPLOAD_MACRO_PARAMETER = "genupload_";
	private static final String IMGUPLOAD_MACRO_PARAMETER = "imgupload_";
	private static final String TEXT_MACRO_PARAMETER = "text_";
	private static final String SWFUPLOAD_MACRO_PARAMETER = "swfupload_";
	private static final String LIST_MACRO_PARAMETER = "list_";
	private static final String HTML_MACRO_PARAMETER = "html_";
	private static final String DIMGUPLOAD_MACRO_PARAMETER = "dimgupload_";
	private static final String RT_MACRO_PARAMETER = "rt_";

	private static final String NEWLINE_CHAR = "\n";
	private static final String DOLLAR = "$";
	private static final String OPEN_BRACE = "{";
	private static final String CLOSE_BRACE = "}";
	private static final String COMMA_DELMITER = ",";
	private static final String TPL_FILE_EXTENSION = ".tpl";
	private static final String RT_FILE_EXTENSION = ".rt";

	// the parameters
	private String advertiserId;
	private String description;
	private String adRefCode;
	/**
	 * Status can be one from the these three possibilities
	 * <ul>
	 * <li><code>active</code> - Would mean <code>0</code></li>
	 * <li><code>inactive</code> - Would mean <code>1</code></li>
	 * <li><code>archived</code> - Would mean <code>2</code></li>
	 * </ul>
	 */
	private String status;
	private String systemId;
	private String adsize;
	private String template;

	protected Map session;
	protected HttpServletRequest request;
	protected HttpServletResponse response;

	@Override
	public void setServletResponse(HttpServletResponse response) {
		this.response = response;
	}

	@Override
	public void setServletRequest(HttpServletRequest request) {
		this.request = request;
	}

	@Override
	public void setSession(Map session) {
		this.session = session;
	}

	public String getAdvertiserId() {
		return advertiserId;
	}

	public void setAdvertiserId(String advertiserId) {
		this.advertiserId = advertiserId;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getAdRefCode() {
		return adRefCode;
	}

	public void setAdRefCode(String adRefCode) {
		this.adRefCode = adRefCode;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public String getSystemId() {
		return systemId;
	}

	public void setSystemId(String systemId) {
		this.systemId = systemId;
	}

	public String getAdsize() {
		return adsize;
	}

	public void setAdsize(String adsize) {
		this.adsize = adsize;
	}

	public String getTemplate() {
		return template;
	}

	public void setTemplate(String template) {
		this.template = template;
	}

	public Map getSession() {
		return session;
	}

	public HttpServletRequest getRequest() {
		return request;
	}

	public HttpServletResponse getResponse() {
		return response;
	}

	/**
	 * This method adds a new Ad into the database.
	 * 
	 * @return Return <code>success</code> if added successfully else returns <code>error</code>
	 */
	public String addNewAd() {
		logger.info("IN method addNewAd() in AddNewAdAction");
		// first trim all the parameters
		trimAllParameters();
		// get the advertiser id from the string received
		int advertiserIdExtracted = getAdvertiserId(advertiserId);
		logger.info("The advertiser id extracted: " + advertiserIdExtracted);
		/*
		 * Have the get the following entities for making the Ad entity Advertiser Adsize AdTemplate
		 */
		Advertiser targetAdvertiserEntity = null;
		Adsize targetAdsizeEntity = null;
		AdTemplate targetAdTemplateEntity = null;
		SystemEntity targetSystemEntity = null;
		AdvertiserDatabaseOperations advertiserDatabaseOperations = new AdvertiserDatabaseOperations();
		List<Advertiser> advertiserEntities = advertiserDatabaseOperations.getAdvertiserEntitiesByAdvertiserId(advertiserIdExtracted);
		for (Advertiser currentAdvertiserEntity : advertiserEntities) {
			if (currentAdvertiserEntity.getAdvertiserId().intValue() == advertiserIdExtracted) {
				targetAdvertiserEntity = currentAdvertiserEntity;
				break;
			}
		}
		int adsizeIdInt = -1;
		try {
			adsizeIdInt = Integer.parseInt(adsize);
		} catch (NumberFormatException numberFormatException) {
			logger.error("Adsizeid is not an integer. Nothing to do. Exception Message: " + numberFormatException.getMessage());
			logger.info("OUT method addNewAd() in AddNewAdAction");
			return ERROR;
		}
		AdsizeDatabaseOperations adsizeDatabaseOperations = new AdsizeDatabaseOperations();
		List<Adsize> adsizeEntities = adsizeDatabaseOperations.getAdsizeEntitiesByAdsizeId(adsizeIdInt);
		for (Adsize currentAdsizeEntity : adsizeEntities) {
			if (currentAdsizeEntity.getAdsizeId().intValue() == adsizeIdInt) {
				targetAdsizeEntity = currentAdsizeEntity;
				break;
			}
		}
		int adTemplateId = -1;
		try {
			adTemplateId = Integer.parseInt(template);
		} catch (NumberFormatException numberFormatException) {
			logger.error("The adTemplateId received is not an integer. Nothing to do. Exception Message: " + numberFormatException.getMessage());
			logger.info("OUT method addNewAd() in AddNewAdAction");
			return ERROR;
		}
		AdTemplateDatabaseOperations adTemplateDatabaseOperations = new AdTemplateDatabaseOperations();
		List<AdTemplate> adTemplateEntities = adTemplateDatabaseOperations.getAdTemplateEntitiesByAdTemplateId(adTemplateId);
		for (AdTemplate currentAdTemplateEntity : adTemplateEntities) {
			if (currentAdTemplateEntity.getAdtplId() == adTemplateId) {
				targetAdTemplateEntity = currentAdTemplateEntity;
				break;
			}
		}
		// get the systemEntity
		SystemDatabaseOperations systemDatabaseOperations = new SystemDatabaseOperations();
		List<SystemEntity> systemEntities = systemDatabaseOperations.getAllSystemEntities();
		for (SystemEntity currentSystemEntity : systemEntities) {
			if (currentSystemEntity.getId().intValue() == Integer.parseInt(systemId)) {
				targetSystemEntity = currentSystemEntity;
			}
		}
		if (targetAdsizeEntity == null || targetAdTemplateEntity == null || targetAdvertiserEntity == null || targetSystemEntity == null) {
			logger.error("One of the required objects is null. Cannot proceed");
			logger.info("OUT method addNewAd() in AddNewAction");
			return ERROR;
		} else {
			short statusShort = -1;
			if (status.equals(STATUS_ACTIVE)) {
				statusShort = 0;
			} else if (status.equals(STATUS_INACTIVE)) {
				statusShort = 1;
			} else if (status.equals(STATUS_ARCHIVED)) {
				statusShort = 2;
			} else {
				logger.error("Unrecognized status: " + status);
				logger.info("OUT method addNewAd() in AddNewAdAction");
				return ERROR;
			}
			// get the template code
			String templateCode = targetAdTemplateEntity.getCode();
			// make the Ad entity
			Ad targetAdEntity = new Ad(adRefCode, description, statusShort, targetAdvertiserEntity, targetAdsizeEntity, targetAdTemplateEntity,
					false, templateCode, new Date(), new Date(), 1);
			targetAdEntity.setSystem(targetSystemEntity);
			// persist this entity
			AdDatabaseOperations adDatabaseOperations = new AdDatabaseOperations();
			adDatabaseOperations.persistAdEntity(targetAdEntity);
			persistInAdPartsTable(targetAdEntity);
		}
		logger.info("OUT method addNewAd() in AddNewAdAction");
		return SUCCESS;
	}

	/**
	 * This method does the persisting the <code>ad_parts</code> table. This method will also make the content to be written to tpl file and rt file
	 */
	private void persistInAdPartsTable(Ad adEntity) {
		logger.info("IN method persistInAdPartsTable() in AddNewAdAction");
		String tplFileContent = "";
		String rtFileContent = "";
		Map<String, Object> parameterMap = request.getParameterMap();
		Iterator<String> parameterMapKeys = parameterMap.keySet().iterator();
		// get all the PartTypes entities
		PartTypesDatabaseOperations partTypesDatabaseOperations = new PartTypesDatabaseOperations();
		List<PartTypes> partTypeEntities = partTypesDatabaseOperations.getAllPartTypesEntities();
		SystemEntity targetSystemEntity = adEntity.getSystem();
		while (parameterMapKeys.hasNext()) {
			String currentParameterName = parameterMapKeys.next();
			if (currentParameterName.startsWith(DIMGUPLOAD_MACRO_PARAMETER)) {
				// TODO: siddharthsaha - to do something when requirements are clear
				logger.info("This is a dimgupload macro parameter.");
				String dimguploadParameterValue = (String) request.getParameter(currentParameterName);
				logger.info("Parameter name: " + currentParameterName + ". ParameterValue: " + dimguploadParameterValue);
				tplFileContent += DOLLAR + OPEN_BRACE + PartTypesConstants.DIMGUPLOAD_PART_TYPE + COMMA_DELMITER + currentParameterName + CLOSE_BRACE
						+ COMMA_DELMITER;
			} else if (currentParameterName.startsWith(GENUPLOAD_MACRO_PARAMETER)) {
				// TODO: siddharthsaha - to do something when requirements are clear
				logger.info("This is a genupload macro parameter");
				String genuploadParameterValue = (String) request.getParameter(currentParameterName);
				logger.info("Parameter Name:" + currentParameterName + ". ParameterValue:" + genuploadParameterValue);
				tplFileContent += DOLLAR + OPEN_BRACE + PartTypesConstants.GENUPLOAD_PART_TYPE + COMMA_DELMITER + currentParameterName + CLOSE_BRACE
						+ COMMA_DELMITER;
			} else if (currentParameterName.startsWith(HTML_MACRO_PARAMETER)) {
				logger.info("This is a html macro parameter");
				String htmlParameterValue = (String) request.getParameter(currentParameterName);
				logger.info("ParameterName: " + currentParameterName + ". ParameterValue: " + htmlParameterValue);
				// get the PartTypes entity for this
				PartTypes htmlPartType = null;
				for (PartTypes currentPartType : partTypeEntities) {
					if (currentPartType.getPartCode().startsWith(PartTypesConstants.HTML_PART_TYPE)) {
						htmlPartType = currentPartType;
						break;
					}
				}
				if (htmlPartType == null) {
					logger.error("Cannot find the PartType entity for " + PartTypesConstants.HTML_PART_TYPE + ". Cannot persist in AdParts");
				} else {
					AdParts htmlAdParts = new AdParts(htmlPartType, htmlPartType.getPartCode(), htmlParameterValue, targetSystemEntity, adEntity);
					AdPartsDatabaseOperations adPartsDatabaseOperations = new AdPartsDatabaseOperations();
					adPartsDatabaseOperations.persistAdPartsEntity(htmlAdParts);
					logger.info("Persisted the html AdParts in the database");
				}
				// make the tpl file content
				tplFileContent += DOLLAR + OPEN_BRACE + PartTypesConstants.HTML_PART_TYPE + COMMA_DELMITER + currentParameterName + COMMA_DELMITER
						+ htmlParameterValue + CLOSE_BRACE + COMMA_DELMITER;
			} else if (currentParameterName.startsWith(IMGUPLOAD_MACRO_PARAMETER)) {
				// TODO: siddharthsaha - to do something when requirements are clear
				logger.info("This is the imgupload macro parameter");
				String imguploadParameterValue = (String) request.getParameter(currentParameterName);
				logger.info("ParameterName: " + currentParameterName + ". ParameterValue: " + imguploadParameterValue);
				tplFileContent += DOLLAR + OPEN_BRACE + PartTypesConstants.IMGUPLOAD_PART_TYPE + COMMA_DELMITER + currentParameterName + CLOSE_BRACE
						+ COMMA_DELMITER;
			} else if (currentParameterName.startsWith(LIST_MACRO_PARAMETER)) {
				logger.info("This is the list macro parameter");
				String listParameterValue = (String) request.getParameter(currentParameterName);
				logger.info("ParameterName: " + currentParameterName + ". ParameterValue: " + listParameterValue);
				PartTypes listPartType = null;
				for (PartTypes currentPartType : partTypeEntities) {
					if (currentPartType.getPartCode().startsWith(PartTypesConstants.LIST_PART_TYPE)) {
						listPartType = currentPartType;
						break;
					}
				}
				if (listPartType == null) {
					logger.error("Cannot find the PartType entity for " + PartTypesConstants.LIST_PART_TYPE + ". Cannot persist in AdParts");
				} else {
					AdParts listAdParts = new AdParts(listPartType, listPartType.getPartCode(), listParameterValue, targetSystemEntity, adEntity);
					AdPartsDatabaseOperations adPartsDatabaseOperations = new AdPartsDatabaseOperations();
					adPartsDatabaseOperations.persistAdPartsEntity(listAdParts);
					logger.info("Persisted the list AdParts in the database");
				}
				// now make the tpl file content
				tplFileContent += DOLLAR + OPEN_BRACE + PartTypesConstants.LIST_PART_TYPE + COMMA_DELMITER + currentParameterName + COMMA_DELMITER
						+ listParameterValue + CLOSE_BRACE + COMMA_DELMITER;
			} else if (currentParameterName.startsWith(NUMERIC_MACRO_PARAMETER)) {
				logger.info("This is the numeric macro parameter");
				String numericParameterValue = (String) request.getParameter(currentParameterName);
				logger.info("ParameterName: " + currentParameterName + ". ParameterValue: " + numericParameterValue);
				PartTypes numericPartType = null;
				for (PartTypes currentPartType : partTypeEntities) {
					if (currentPartType.getPartCode().startsWith(PartTypesConstants.NUMERIC_PART_TYPE)) {
						numericPartType = currentPartType;
						break;
					}
				}
				if (numericPartType == null) {
					logger.error("Cannot find the PartType entity for " + PartTypesConstants.NUMERIC_PART_TYPE + ". Cannot persist in AdParts");
				} else {
					AdParts numericAdParts = new AdParts(numericPartType, numericPartType.getPartCode(), numericParameterValue, targetSystemEntity,
							adEntity);
					AdPartsDatabaseOperations adPartsDatabaseOperations = new AdPartsDatabaseOperations();
					adPartsDatabaseOperations.persistAdPartsEntity(numericAdParts);
					logger.info("Persisted the numeric AdParts in the database");
				}
				// now make the tpl code
				tplFileContent += DOLLAR + OPEN_BRACE + PartTypesConstants.NUMERIC_PART_TYPE + COMMA_DELMITER + currentParameterName + COMMA_DELMITER
						+ numericParameterValue + CLOSE_BRACE + COMMA_DELMITER;
			} else if (currentParameterName.startsWith(SWFUPLOAD_MACRO_PARAMETER)) {
				// TODO: siddharthsaha - to do something when requirements are clear
				logger.info("The is the swfupload macro parameter");
				String swfuploadParameter = (String) request.getParameter(currentParameterName);
				logger.info("ParameterName: " + currentParameterName + ". ParameterValue: " + swfuploadParameter);
				tplFileContent += DOLLAR + OPEN_BRACE + PartTypesConstants.SWFUPLOAD_PART_TYPE + COMMA_DELMITER + currentParameterName + CLOSE_BRACE
						+ COMMA_DELMITER;
			} else if (currentParameterName.startsWith(TEXT_MACRO_PARAMETER)) {
				logger.info("This is the text macro parameter");
				String textParameterValue = (String) request.getParameter(currentParameterName);
				logger.info("ParameterName: " + currentParameterName + ". ParameterValue: " + textParameterValue);
				PartTypes textPartType = null;
				for (PartTypes currentPartType : partTypeEntities) {
					if (currentPartType.getPartCode().startsWith(PartTypesConstants.TEXT_PART_TYPE)) {
						textPartType = currentPartType;
						break;
					}
				}
				if (textPartType == null) {
					logger.error("Cannot find the PartType entity for " + PartTypesConstants.TEXT_PART_TYPE + ". Cannot persist in AdParts");
				} else {
					AdParts textAdParts = new AdParts(textPartType, textPartType.getPartCode(), textParameterValue, targetSystemEntity, adEntity);
					AdPartsDatabaseOperations adPartsDatabaseOperations = new AdPartsDatabaseOperations();
					adPartsDatabaseOperations.persistAdPartsEntity(textAdParts);
					logger.info("Persisted the text AdParts in the database");
				}
				// now make the adtpl File content
				tplFileContent += DOLLAR + OPEN_BRACE + PartTypesConstants.TEXT_PART_TYPE + COMMA_DELMITER + currentParameterName + COMMA_DELMITER
						+ textParameterValue + CLOSE_BRACE + COMMA_DELMITER;
			} else if (currentParameterName.startsWith(RT_MACRO_PARAMETER)) {
				logger.info("This is the rt macro parameter");
				String rtParameterValue = (String) request.getParameter(currentParameterName);
				// this will be written to the rt file
				rtFileContent += DOLLAR + OPEN_BRACE + PartTypesConstants.RT_PART_TYPE + COMMA_DELMITER + currentParameterName + COMMA_DELMITER
						+ rtParameterValue + CLOSE_BRACE + NEWLINE_CHAR;
				tplFileContent += DOLLAR + OPEN_BRACE + PartTypesConstants.RT_PART_TYPE + COMMA_DELMITER + currentParameterName + CLOSE_BRACE
						+ COMMA_DELMITER;
			}
		}
		try {
			makeTemplateFile(adEntity.getAdId().intValue(), tplFileContent);
		} catch (FileNotFoundException fileNotFoundException) {
			logger.error("FileNotFoundException while making the template file. Message: " + fileNotFoundException.getMessage());
			logger.error(fileNotFoundException);
		} catch (IOException ioException) {
			logger.error("IOException while writing the template file. Message: " + ioException.getMessage());
			logger.error(ioException);
		}
		try {
			makeRtFile(adEntity.getAdId().intValue(), rtFileContent);
		} catch (FileNotFoundException fileNotFoundException) {
			logger.error("FileNotFoundException while making the rt file. Message: " + fileNotFoundException.getMessage());
			logger.error(fileNotFoundException);
		} catch (IOException ioException) {
			logger.error("IOException while writing to the rt file. Message: " + ioException.getMessage());
			logger.error(ioException);
		}
		logger.info("OUT method persistInAdPartsTable() in AddNewAdAction");
	}

	/**
	 * This method will make the adtpl file.
	 * 
	 * @param adId
	 *        The id of the ad
	 * @param tplFileContent
	 *        The template file content
	 */
	private void makeTemplateFile(int adId, String tplFileContent) throws FileNotFoundException, IOException {
		logger.info("IN method makeTemplateFile() in AddNewAdAction");
		String currentWorkingDirectory = System.getProperty("user.dir"); // var/lib/tomcat6
		logger.info("Current Working directory in makeTemplateFile(): " + currentWorkingDirectory);
		String directoryOfFile = currentWorkingDirectory + File.separator + "webapps" + File.separator + "StrutsProject" + File.separator;
		String templateFileName = adId + TPL_FILE_EXTENSION;
		templateFileName = directoryOfFile + templateFileName;
		logger.info("Making the template file at: " + templateFileName);
		PrintWriter templateFileWriter = new PrintWriter(new File(templateFileName));
		// remove the last comman from tplFileContent
		if (tplFileContent.endsWith(COMMA_DELMITER)) {
			tplFileContent = tplFileContent.substring(0, tplFileContent.length() - 1);
		}
		templateFileWriter.println(tplFileContent);
		templateFileWriter.flush();
		templateFileWriter.close();
		logger.info("OUT method makeTemplateFile() in AddNewAdAction");
	}

	/**
	 * This makes the rt file
	 * 
	 * @param adId
	 *        The id of the ad
	 * @param rtFileContent
	 *        The file content of the rt file
	 */
	private void makeRtFile(int adId, String rtFileContent) throws FileNotFoundException, IOException {
		logger.info("IN method makeRtFile() in AddNewAction");
		String currentWorkingDirectory = System.getProperty("user.dir"); // var/lib/tomcat6
		logger.info("Current Working directory in makeRtFile(): " + currentWorkingDirectory);
		String directoryOfFile = currentWorkingDirectory + File.separator + "webapps" + File.separator + "StrutsProject" + File.separator;
		String rtFileName = adId + RT_FILE_EXTENSION;
		rtFileName = directoryOfFile + rtFileName;
		logger.info("Making the rt file name at: " + rtFileName);
		PrintWriter rtFileWriter = new PrintWriter(new File(rtFileName));
		rtFileWriter.println(rtFileContent);
		rtFileWriter.flush();
		rtFileWriter.close();
		logger.info("OUT method makeRtFile() in AddNewAction");
	}

	/**
	 * This method extracts the advertiserId from the parameter received. This will be in proper format as it has been validated using AJAX
	 * 
	 * @param parameterAdvertiserId
	 * @return
	 */
	private int getAdvertiserId(String parameterAdvertiserId) {
		logger.info("IN method getAdvertiserId() in AddNewAdAction");
		int advertiserId = -1;
		parameterAdvertiserId = parameterAdvertiserId.substring(0, parameterAdvertiserId.length() - 1);
		int indexOfLastStartBracket = parameterAdvertiserId.lastIndexOf(START_BRACKET);
		parameterAdvertiserId = parameterAdvertiserId.substring(indexOfLastStartBracket + 1);
		advertiserId = Integer.parseInt(parameterAdvertiserId);
		logger.info("OUT method getAdvertiserId() in AddNewAdAction");
		return advertiserId;
	}

	/**
	 * This method trims all the parameters of any extra spaces if any
	 */
	private void trimAllParameters() {
		logger.info("IN method trimAllParameters() in AddNewAction");
		if (advertiserId != null) {
			advertiserId = advertiserId.trim();
		}
		if (description != null) {
			description = description.trim();
		}
		if (adRefCode != null) {
			adRefCode = adRefCode.trim();
		}
		if (status != null) {
			status = status.trim();
		}
		if (systemId != null) {
			systemId = systemId.trim();
		}
		if (adsize != null) {
			adsize = adsize.trim();
		}
		if (template != null) {
			template = template.trim();
		}
		logger.info("OUT method trimAllParameters() in AddNewAction");
	}

}
